/////////////////////////////////////////////////////////////////////////////////////
// Include Files
#include "Managers\TextureManager.h"

#include "Rendering\GLExt.h"

#include <math.h>

#include <assert.h>

/////////////////////////////////////////////////////////////////////////////////////
// ISingleton<CTextureManager> Static Initialization
CTextureManager * ISingleton<CTextureManager>::m_pInstance = CE_NULL;

/////////////////////////////////////////////////////////////////////////////////////
// CTextureManager Implementation
void CTextureManager::Initialize()
{
}

void CTextureManager::Shutdown() { }

void CTextureManager::LoadTextureFile( string_t _strFilename )
{
	// open the file
	file_t TextureFile;
	if( TextureFile.Open(L"..\\Galleons\\Textures\\" + _strFilename, L"rb") )
	{
		// the texture load event argument;
		CTextureEventArg *pArg = NULL;

		// check the extension to find the file type
		string_t strExt = _strFilename.Substring(_strFilename.LastIndexOf('.') + 1);
		if( strExt == L"tex2d" )
		{
			// read the width, height, and mip count
			uint32_t uWidth    = TextureFile.ReadUInt32();
			uint32_t uHeight   = TextureFile.ReadUInt32();
			uint32_t uMipCount = TextureFile.ReadUInt32();

			// create the event argument
			pArg = new CTextureEventArg( 
				_strFilename, TEXTURE_TYPE_2D, 1, uMipCount, uWidth, uHeight 
				);

			// add the image to the argument
			pArg->AddImage( TEXTURE_TARGET_2D );

			// read all mips
			uint_t uMipWidth  = uWidth;
			uint_t uMipHeight = uHeight;
			for( uint_t uMip = 0; uMip < uMipCount; ++uMip )
			{
				// read the mip
				uint_t uSize = uMipWidth * uMipHeight * 3;
				byte_t *pData = new byte_t[uSize];
				TextureFile.Read(pData, uSize);

				// add the mip data to the argument
				pArg->AddImageMip(pData);

				// the next mip is half the size
				uMipWidth  /= 2;
				uMipHeight /= 2;
			}
		}
		else if( strExt == L"texcube" )
		{
			// read the width, height, and mip count
			uint32_t uWidth    = TextureFile.ReadUInt32();
			uint32_t uHeight   = TextureFile.ReadUInt32();
			uint32_t uMipCount = TextureFile.ReadUInt32();

			// create the event argument
			pArg = new CTextureEventArg( 
				_strFilename, TEXTURE_TYPE_CUBE, 6, uMipCount, uWidth, uHeight 
				);

			// read each image
			for( uint_t uImage = 0; uImage < 6; ++uImage )
			{
				// add an image to the argument
				pArg->AddImage( texture_e(TEXTURE_TARGET_CUBE_POS_X + uImage) );

				// read all mips
				uint_t uMipWidth  = uWidth;
				uint_t uMipHeight = uHeight;
				for( uint_t uMip = 0; uMip < uMipCount; ++uMip )
				{
					// read the mip
					uint_t uSize = uMipWidth * uMipHeight * 3;
					byte_t *pData = new byte_t[uSize];
					TextureFile.Read(pData, uSize);

					// add the mip data to the argument
					pArg->AddImageMip(pData);

					// the next mip is half the size
					uMipWidth  /= 2;
					uMipHeight /= 2;
				}
			}
		}
		else
		{
			// unknown extension
			assert(false);
		}

		// close the file
		TextureFile.Close();

		// raise the texture load event
		TextureLoad.Raise(pArg);
	}
	else
	{
		// the file could not be opened
		assert(false);
	}
}

void CTextureManager::CreateTexture( CTextureEventArg const &_rArg )
{
	// check for errors
#ifdef _DEBUG
	GLenum eError2 = glGetError();
	assert( eError2 == GL_NO_ERROR );
#endif // _DEBUG

	// get the name, type, and image count
	string_t  strName     = _rArg.GetName();
	texture_e eType       = _rArg.GetType();
	uint_t    uImageCount = _rArg.GetImageCount();
	uint_t    uMipCount   = _rArg.GetMipCount();
	uint_t    uWidth      = _rArg.GetWidth();
	uint_t    uHeight     = _rArg.GetHeight();

	// create and bind a new texture
	uint_t uiTexture;
	glGenTextures(1, &uiTexture);
	glBindTexture((GLenum)eType, uiTexture);

	// check for errors
#ifdef _DEBUG
	GLenum eError = glGetError();
	assert( eError == GL_NO_ERROR );
#endif // _DEBUG

	// load each level of each texture into OpenGL
	for( uint_t uImage = 0; uImage < uImageCount; ++uImage )
	{
		// activate the current image
		_rArg.ActiveImage(uImage);

		// get the width, height, target, and mip count
		uint_t uMipWidth  = uWidth;
		uint_t uMipHeight = uHeight;
		texture_e eTarget = _rArg.GetImageTarget();

		// load all mips
		for( uint_t uMip = 0; uMip < uMipCount; ++uMip )
		{
			// create the texture
			glTexImage2D( eTarget, uMip, GL_RGB, uMipWidth, uMipHeight, 0, GL_RGB, 
				GL_UNSIGNED_BYTE, _rArg.GetImageMip(uMip) );

			// check for errors
#ifdef _DEBUG
			GLenum eError = glGetError();
			assert( eError == GL_NO_ERROR );
#endif // _DEBUG

			// the next level has half the width and height
			uMipWidth  /= 2;
			uMipHeight /= 2;
		}
	}

	// add the texture to the list
	TTexture tex = { strName, uiTexture };
	m_lstTextures.Append(tex);

	// check for errors
#ifdef _DEBUG
	GLenum eError3 = glGetError();
	assert( eError3 == GL_NO_ERROR );
#endif // _DEBUG
}

void CTextureManager::DestroyTexture( string_t _strName )
{
	for( uint_t uTex = 0; uTex < m_lstTextures.GetSize(); ++uTex )
	{
		if( m_lstTextures[uTex].strName == _strName )
		{
			glDeleteTextures(1, &m_lstTextures[uTex].uTexture);
			return;
		}
	}
	assert(false);
}

uint_t CTextureManager::GetTexture( string_t _strName )
{
	for( uint_t i = 0; i < m_lstTextures.GetSize(); ++i )
	{
		if( m_lstTextures[i].strName == _strName )
		{
			return m_lstTextures[i].uTexture;
		}
	}
	assert(false);
	return 0;
}
